package com.kxmall.common.helper;

import cn.dev33.satoken.stp.StpUtil;
import com.kxmall.common.enums.DeviceType;
import com.kxmall.common.enums.UserType;
import com.kxmall.common.exception.UtilException;
import com.kxmall.common.utils.TokenUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Token返回助手
 * 确保登录成功返回的Token唯一性和安全性
 *
 * @author kxmall
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TokenReturnHelper {

    /**
     * 安全获取Token用于返回
     * 在返回Token前进行最后的唯一性和安全性验证
     */
    public static String getSecureTokenForReturn(String expectedUserType, DeviceType expectedDeviceType, Long expectedUserId) {
        try {
            // 1. 根据用户类型和ID获取对应的Token
            String tokenValue = TokenUtils.getTokenByUserTypeAndId(expectedUserType, expectedDeviceType, expectedUserId);

            if (tokenValue == null || tokenValue.length() < 32) {
                throw new UtilException("Token获取失败或格式不正确");
            }

            // 2. 验证Token与用户绑定
            String actualLoginId = StpUtil.getLoginIdAsString();
            String expectedLoginId = expectedUserType + ":" + expectedUserId;
            if (!expectedLoginId.equals(actualLoginId)) {
                throw new UtilException("Token用户绑定验证失败 - 期望: " + expectedLoginId + ", 实际: " + actualLoginId);
            }

            // 3. 验证Token与设备绑定
            String actualDevice = StpUtil.getLoginDevice();
            if (!expectedDeviceType.getDevice().equals(actualDevice)) {
                throw new UtilException("Token设备绑定验证失败 - 期望: " + expectedDeviceType.getDevice() + ", 实际: " + actualDevice);
            }

            // 4. 验证Token会话完整性
            if (StpUtil.getTokenSession().get("loginUser") == null) {
                throw new UtilException("Token会话信息不完整");
            }

            // 5. 验证Token有效期
            long tokenTimeout = StpUtil.getTokenTimeout();
            if (tokenTimeout <= 0) {
                throw new UtilException("Token已过期");
            }

            // 6. 记录Token返回日志
            logTokenReturn(tokenValue, expectedUserType, expectedDeviceType, expectedUserId);

            // 7. 返回验证通过的Token
            return tokenValue;

        } catch (Exception e) {
            log.error("🚨 Token返回安全验证失败", e);
            throw new UtilException("Token返回安全验证失败: " + e.getMessage(), e);
        }
    }

    /**
     * APP用户Token安全返回
     * 根据用户ID获取对应的APP用户Token
     */
    public static String getAppUserToken(Long userId) {
        log.debug("获取APP用户Token - 用户ID: {}", userId);
        return getSecureTokenForReturn(UserType.APP_USER.getUserType(), DeviceType.APP, userId);
    }

    /**
     * 骑手用户Token安全返回
     * 根据用户ID获取对应的骑手用户Token
     */
    public static String getRiderUserToken(Long userId) {
        log.debug("获取骑手用户Token - 用户ID: {}", userId);
        return getSecureTokenForReturn(UserType.RIDER_USER.getUserType(), DeviceType.RIDER, userId);
    }

    /**
     * 系统用户Token安全返回
     * 根据用户ID获取对应的系统用户Token
     */
    public static String getSysUserToken(Long userId) {
        log.debug("获取系统用户Token - 用户ID: {}", userId);
        return getSecureTokenForReturn(UserType.SYS_USER.getUserType(), DeviceType.PC, userId);
    }

    /**
     * 验证Token返回前的最终检查
     */
    public static void validateTokenBeforeReturn(String tokenValue, String userType, DeviceType deviceType, Long userId) {
        try {
            // 1. Token格式验证
            if (tokenValue == null || tokenValue.length() < 32) {
                throw new UtilException("Token格式验证失败");
            }

            // 2. Token内容一致性验证
            String currentToken = StpUtil.getTokenValue();
            if (!tokenValue.equals(currentToken)) {
                throw new UtilException("Token一致性验证失败");
            }

            // 3. 用户信息一致性验证
            String currentLoginId = StpUtil.getLoginIdAsString();
            String expectedLoginId = userType + ":" + userId;
            if (!expectedLoginId.equals(currentLoginId)) {
                throw new UtilException("用户信息一致性验证失败");
            }

            // 4. 设备信息一致性验证
            String currentDevice = StpUtil.getLoginDevice();
            if (!deviceType.getDevice().equals(currentDevice)) {
                throw new UtilException("设备信息一致性验证失败");
            }

            log.debug("✅ Token返回前验证通过 - 用户: {}, 设备: {}, Token前缀: {}", 
                expectedLoginId, deviceType.getDevice(), 
                tokenValue.substring(0, Math.min(10, tokenValue.length())) + "...");

        } catch (Exception e) {
            log.error("🚨 Token返回前验证失败", e);
            throw new UtilException("Token返回前验证失败: " + e.getMessage(), e);
        }
    }

    /**
     * 检查Token唯一性
     */
    public static boolean checkTokenUniqueness(String tokenValue, String userType, DeviceType deviceType) {
        try {
            // 1. 检查Token是否为当前会话的Token
            String currentToken = StpUtil.getTokenValue();
            if (!tokenValue.equals(currentToken)) {
                log.warn("🚨 Token唯一性检查失败 - 不是当前会话Token");
                return false;
            }

            // 2. 检查Token是否与用户类型匹配
            String currentLoginId = StpUtil.getLoginIdAsString();
            if (!currentLoginId.startsWith(userType + ":")) {
                log.warn("🚨 Token唯一性检查失败 - 用户类型不匹配");
                return false;
            }

            // 3. 检查Token是否与设备类型匹配
            String currentDevice = StpUtil.getLoginDevice();
            if (!deviceType.getDevice().equals(currentDevice)) {
                log.warn("🚨 Token唯一性检查失败 - 设备类型不匹配");
                return false;
            }

            return true;

        } catch (Exception e) {
            log.error("Token唯一性检查异常", e);
            return false;
        }
    }

    /**
     * 记录Token返回日志
     */
    private static void logTokenReturn(String tokenValue, String userType, DeviceType deviceType, Long userId) {
        try {
            String tokenPrefix = tokenValue.substring(0, Math.min(10, tokenValue.length())) + "...";
            long tokenTimeout = StpUtil.getTokenTimeout();
            
            log.info("🔑 Token安全返回 - 用户类型: {}, 设备类型: {}, 用户ID: {}, Token前缀: {}, 剩余时间: {}秒", 
                userType, deviceType.getDevice(), userId, tokenPrefix, tokenTimeout);

            // 记录Token返回统计
            System.out.println(String.format(
                "📤 Token返回记录 - 用户类型: %s, 设备: %s, 用户ID: %s, Token前缀: %s, 时间: %d",
                userType, deviceType.getDevice(), userId, tokenPrefix, System.currentTimeMillis()
            ));

        } catch (Exception e) {
            log.error("记录Token返回日志异常", e);
        }
    }

    /**
     * 生成Token返回摘要信息
     */
    public static String generateTokenSummary(String tokenValue, String userType, DeviceType deviceType, Long userId) {
        try {
            String tokenPrefix = tokenValue != null ? tokenValue.substring(0, Math.min(10, tokenValue.length())) + "..." : "null";
            long tokenTimeout = StpUtil.getTokenTimeout();
            
            return String.format(
                "Token摘要 - 用户: %s:%s, 设备: %s, 前缀: %s, 有效期: %d秒",
                userType, userId, deviceType.getDevice(), tokenPrefix, tokenTimeout
            );

        } catch (Exception e) {
            log.error("生成Token返回摘要异常", e);
            return "Token摘要生成失败";
        }
    }



    /**
     * 清理无效Token
     */
    public static void cleanupInvalidTokens(String userType, DeviceType deviceType, Long userId) {
        TokenUtils.cleanupUserTokens(userType, deviceType, userId, 3);
    }
}
